﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace Jinndev {

    static class UpdateMode {


        static Config config;
        static byte[] buffer = new byte[409600];
        static int retryCount = 3;


        public static void Update(string[] args) {
            Console.WriteLine("[Update] " + string.Join(" ", args));

            string exe = Util.FindArg(args, "-exe");
            bool isCheck = Util.HasAnyArgs(args, "-c", "-check");

            Console.WriteLine("exe: " + exe);
            Console.WriteLine("isCheck: " + isCheck);

            if (!File.Exists("Updater.json")) {
                Console.WriteLine("[Error] \"Updater.json\" not found");
                return;
            }
            string text = File.ReadAllText("Updater.json", Encoding.UTF8);
            config = JsonSerializer.Deserialize<Config>(text);

            PublishInfo publishInfo = PullFileList().Result;
            if (publishInfo == null) {
                return;
            }

            if (isCheck) {
                // 检查更新命令，则只输出信息
                Console.WriteLine("[PublishInfo] Start");
                Console.WriteLine(JsonSerializer.Serialize(publishInfo));
                Console.WriteLine("[PublishInfo] End");
                return;
            }

            // 对比版本
            if (publishInfo.version == config.version) {
                return;
            }

            // 关闭目标程序
            ShutdownExe(exe);

            // 下载
            bool success = Download(publishInfo).Result;
            if (!success) {
                return;
            }

            // 重启目标程序
            RunExe(exe);
        }

        static async Task<PublishInfo> PullFileList() {
            string url = Path.Combine(config.server, "PublishInfo.json").Replace("\\", "/");

            HttpWebRequest request = WebRequest.CreateHttp(url);
            try {
                HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse;

                PublishInfo publishInfo = null;
                using (Stream stream = response.GetResponseStream()) {
                    publishInfo = await JsonSerializer.DeserializeAsync<PublishInfo>(stream);
                }
                return publishInfo;

            }
            catch (Exception e) {
                Console.WriteLine("[Error] " + e.Message);
            }
            return null;
        }

        static async Task<bool> Download(PublishInfo publishInfo) {
            // 下载到临时目录
            string cacheDir = "download_cache";
            if (!Directory.Exists(cacheDir)) {
                Directory.CreateDirectory(cacheDir);
            }

            for (int i = 0; i < publishInfo.entries.Count; i++) {
                FileEntry entry = publishInfo.entries[i];

                // 对比本地文件，是否需要更新
                string localFile = entry.path;
                if (File.Exists(localFile)) {
                    string md5 = Util.GetFileMD5(localFile);
                    if (md5 == entry.md5) {
                        continue; // 不需要更新
                    }
                }

                // 下载到临时目录
                string saveFile = Path.Combine(cacheDir, entry.path);
                if (File.Exists(saveFile)) {
                    // 已存在文件，则对比md5
                    string md5 = Util.GetFileMD5(saveFile);
                    if (md5 == entry.md5) {
                        Console.WriteLine("[Downloaded] " + entry.path);
                        continue; // 已下载
                    }
                    File.Delete(saveFile);
                }

                Console.WriteLine("[Downloading] " + entry.path);
                bool success = await DownloadFile(entry, saveFile);

                if (success) {
                    retryCount = 3;
                }
                else if (retryCount > 0) {
                    retryCount--;
                    i--;
                    Console.WriteLine("[Retry] " + entry.path);
                    await Task.Delay(1000);
                }
                else {
                    return false;
                }
            }

            try {
                // 从临时目录移动出来
                string[] files = Directory.GetFiles(cacheDir, "*.*", SearchOption.AllDirectories);
                foreach (string file in files) {
                    string destFile = file.Substring(cacheDir.Length + 1);
                    File.Move(file, destFile, true);
                }
                Directory.Delete(cacheDir, true);
            }
            catch (Exception e) {
                Console.WriteLine("[Error] Move files fail: " + e.Message);
                return false;
            }

            try {
                // 更新本地版本信息
                config.version = publishInfo.version;
                File.WriteAllText("Updater.json", JsonSerializer.Serialize(config), Encoding.UTF8);
            }
            catch (Exception e) {
                Console.WriteLine("[Error] Update version fail: " + e.Message);
                return false;
            }

            return true;
        }

        static async Task<bool> DownloadFile(FileEntry entry, string saveFile) {
            string url = Path.Combine(config.server, entry.path).Replace("\\", "/");

            HttpWebRequest request = WebRequest.CreateHttp(url);
            try {
                HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse;

                using (Stream stream = response.GetResponseStream()) {
                    Util.PrepareFileDir(saveFile);
                    using (Stream fileStream = File.Create(saveFile)) {
                        int length = 0;
                        while ((length = stream.Read(buffer, 0, buffer.Length)) > 0) {
                            fileStream.Write(buffer, 0, length);
                        }
                    }
                }
                return true;
            }
            catch (Exception e) {
                Console.WriteLine($"[Error] Download \"{entry.path}\" fail: " + e.Message);
            }
            return false;
        }

        static void ShutdownExe(string exe) {
            if (string.IsNullOrEmpty(exe)) {
                return;
            }
            try {
                Process[] arrayProcess = Process.GetProcessesByName(exe);
                foreach (var p in arrayProcess) {
                    Console.WriteLine("[Shutdown] " + p.ProcessName);
                    p.Kill();
                }
            }
            catch (Exception e) {
                Console.WriteLine($"[Error] Shutdown \"{exe}\" fail: {e.Message}");
            }
        }

        static void RunExe(string exe) {
            if (string.IsNullOrEmpty(exe)) {
                return;
            }
            try {
                Console.WriteLine("[Start] " + exe);
                Process.Start(exe);
            }
            catch (Exception e) {
                Console.WriteLine($"[Error] Start \"{exe}\" fail: {e.Message}");
            }
        }

    }

}
